package com.seckill.service.impl;

import cn.hutool.json.JSONUtil;
import com.seckill.constants.LoginConstant;
import com.seckill.dao.MiaoshaUserDao;
import com.seckill.domain.MiaoshaUser;
import com.seckill.exception.GlobalException;
import com.seckill.redis.CacheDeleteHandler;
import com.seckill.redis.MiaoshaUserKey;
import com.seckill.redis.RedisService;
import com.seckill.result.CodeMsg;
import com.seckill.service.MiaoshaUserService;
import com.seckill.util.Md5Util;
import com.seckill.util.UUIDUtil;
import com.seckill.vo.LoginVo;
import java.util.Objects;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author yangjunwei
 * @date 2021/10/29 3:43 下午
 */
@Service
@Slf4j
public class MiaoshaUserServiceImpl implements MiaoshaUserService {

    @Autowired
    MiaoshaUserDao miaoshaUserDao;

    @Autowired
    RedisService redisService;

    @Override
    public boolean login(HttpServletResponse httpServletResponse, LoginVo loginVo) {
        //根据手机号查询用户
        MiaoshaUser miaoshaUser = selectMiaoshaUserById(Long.valueOf(loginVo.getMobile()));
        if (Objects.isNull(miaoshaUser)) {
            //手机号不存在
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }
        //校验密码
        checkLoginPassword(loginVo.getPassword(), miaoshaUser.getPassword(), miaoshaUser.getSalt());
        //缓存用户信息
        addLoginUserInfo(httpServletResponse, UUIDUtil.uuid(), miaoshaUser);
        //登陆成功
        return true;
    }

    @Override
    public boolean updatePassword(String token, Long userId, String oldPassword, String newPassword) {
        //1.从数据库查数据
        MiaoshaUser miaoshaUser = miaoshaUserDao.selectMiaoshaUserById(userId);
        if (Objects.isNull(miaoshaUser)) {
            //触发全局异常
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }
        //检验旧密码是否一致
        checkLoginPassword(oldPassword, miaoshaUser.getPassword(), miaoshaUser.getSalt());
        //加密密码
        String dbPassword = Md5Util.formPassToDBPass(newPassword, miaoshaUser.getSalt());

        //2.更新数据库
        MiaoshaUser newMiaoshaUser = new MiaoshaUser();
        newMiaoshaUser.setId(userId);
        newMiaoshaUser.setPassword(dbPassword);
        miaoshaUserDao.updateMiaoshaUser(newMiaoshaUser);

        //3.更新缓存
        //采取删除重试的机制保证缓存删除
        this.deleteCacheLoginInfo(userId);
        miaoshaUser.setPassword(dbPassword);
        //更新根据token查用户信息的缓存
        redisService.set(MiaoshaUserKey.token, token, miaoshaUser);
        return true;
    }

    @Override
    public MiaoshaUser selectMiaoshaUserById(Long userId) {
        MiaoshaUser miaoshaUser = new MiaoshaUser();
        try {
            miaoshaUser = redisService.get(MiaoshaUserKey.getUserById, "" + userId, MiaoshaUser.class);
            if (Objects.nonNull(miaoshaUser)) {
                //用户信息缓存命中，直接返回
                return miaoshaUser;
            }
            //对象缓存(注意缓存更新)
            miaoshaUser = miaoshaUserDao.selectMiaoshaUserById(userId);
            if (Objects.nonNull(miaoshaUser)) {
                redisService.set(MiaoshaUserKey.getUserById, "" + userId, miaoshaUser);
            }
        } catch (Exception e) {
            log.error("selectMiaoshaUserById error =====> {}", e.getMessage());
        }
        return miaoshaUser;
    }

    @Override
    public MiaoshaUser getMiaoshaUserByToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        //从redis缓存中查询用户信息
        return redisService.get(MiaoshaUserKey.token, token, MiaoshaUser.class);
    }

    /**
     * 校验密码
     *
     * @param formPassword 表单提交密码
     * @param userPassword 数据库用户密码
     * @param salt         盐值
     */
    private void checkLoginPassword(String formPassword, String userPassword, String salt) {
        //校验密码
        String formPassToDbPass = Md5Util.formPassToDBPass(formPassword, salt);
        if (!StringUtils.equalsIgnoreCase(formPassToDbPass, userPassword)) {
            //密码错误
            throw new GlobalException(CodeMsg.PASSWORD_ERROR);
        }
    }

    /**
     * 添加登陆信息 1.redis 2.cookie
     */
    @Override
    public void addLoginUserInfo(HttpServletResponse httpServletResponse, String token, MiaoshaUser miaoshaUser) {
        try {
            //redis缓存登陆信息
            redisService.set(MiaoshaUserKey.token, token, miaoshaUser);
            //添加cookie
            Cookie cookie = new Cookie(LoginConstant.COOKIE_NAME, token);
            //设置cookie过期时间，与redis过期时间保持一致
            cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
            cookie.setPath("/");
            httpServletResponse.addCookie(cookie);
        } catch (Exception e) {
            log.error("add loginUser token error! userInfo:{}", JSONUtil.toJsonStr(miaoshaUser), e);
        }
    }

    /**
     * 缓存删除重试
     */
    private void deleteCacheLoginInfo(Long userId) {
        boolean delete = redisService.delete(MiaoshaUserKey.getUserById, "" + userId);
        if (delete) {
            return;
        }
        //删除不成功，3s后重试
        CacheDeleteHandler.addCacheDeleteTask(MiaoshaUserKey.getUserById, "" + userId, 3);
    }


}
